package org.testng.eclipse.util.param;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.ArrayInitializer;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.MarkerAnnotation;
import org.eclipse.jdt.core.dom.MemberValuePair;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import com.google.common.base.Strings;
/**
* Visitor for TestNG methods.
*/
public class TestNGMethodParameterVisitor extends ASTVisitor {
private static final String ANNOTATION_PACKAGE = "org.testng.annotations.";
private static final String PARAMETER_ANNOTATION = "Parameters";
private static final String PARAMETER_ANNOTATION_FQN = ANNOTATION_PACKAGE + PARAMETER_ANNOTATION;
private static final String CONFIGURATION_ANNOTATION = "Configuration";
private static final String CONFIGURATION_ANNOTATION_FQN = ANNOTATION_PACKAGE + CONFIGURATION_ANNOTATION;
private static final String TEST_ANNOTATION = "Test";
private static final String TEST_ANNOTATION_FQN = ANNOTATION_PACKAGE + TEST_ANNOTATION;
private static final String DEFAULT_PARAM_VALUE = "param-val-not-found";
private Map<MethodDeclaration, List<String>> m_parameters = new HashMap<>();
private IType m_typeFilter;
private IMethod m_methodFilter;
public TestNGMethodParameterVisitor() {
}
public TestNGMethodParameterVisitor(IMethod methodOnly) {
m_methodFilter= methodOnly;
}
public TestNGMethodParameterVisitor(IType typeOnly) {
m_typeFilter= typeOnly;
}
@Override
public boolean visit(TypeDeclaration node) {
if(null != m_typeFilter) {
return node.getName().getIdentifier().equals(m_typeFilter.getElementName());
}
else {
return true;
}
}
@Override
public boolean visit(MethodDeclaration node) {
if(null != m_methodFilter) {
return node.getName().getIdentifier().equals(m_methodFilter.getElementName())
&& node.parameters().size() == m_methodFilter.getNumberOfParameters();
}
else {
return true;
}
}
/**
* No MarkerAnnotated method can have parameters. Ignore.
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.MarkerAnnotation)
*/
@Override
public boolean visit(MarkerAnnotation annotation) {
return false;
}
@Override
public boolean visit(NormalAnnotation annotation) {
if(!isKnownAnnotation(annotation.getTypeName().getFullyQualifiedName())) {
return false;
}
List<?> values= annotation.values();
if(null != values && !values.isEmpty()) {
for(int i= 0; i < values.size(); i++) {
MemberValuePair pair= (MemberValuePair) values.get(i);
if("parameters".equals(pair.getName().toString())) {
Expression paramAttr= pair.getValue();
if(paramAttr instanceof ArrayInitializer) {
record((MethodDeclaration) annotation.getParent(), (ArrayInitializer) paramAttr);
} else if (paramAttr instanceof StringLiteral) {
record((MethodDeclaration) annotation.getParent(), (StringLiteral) paramAttr);
}
}
}
}
return false;
}
/**
* Only Parameters annotation can be used to describe the parameters.
*
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.SingleMemberAnnotation)
*/
@Override
public boolean visit(SingleMemberAnnotation annotation) {
if(isParameterAnnotation(annotation.getTypeName().getFullyQualifiedName())) {
Expression paramValues= annotation.getValue();
if (paramValues instanceof ArrayInitializer) {
record((MethodDeclaration) annotation.getParent(), (ArrayInitializer) paramValues);
} else if (paramValues instanceof StringLiteral) {
record((MethodDeclaration) annotation.getParent(), (StringLiteral) paramValues);
}
}
return false;
}
public boolean hasParameters() {
return !m_parameters.isEmpty();
}
private void record(MethodDeclaration method, List<String> paramNames) {
m_parameters.put(method, paramNames);
}
protected void record(MethodDeclaration method, StringLiteral value){
if (! Strings.isNullOrEmpty(value.getLiteralValue())) {
List<String> paramNames = new ArrayList<String>();
paramNames.add(value.getLiteralValue());
record(method, paramNames);
}
}
/**
*
* @param method the known TestNG annotation with required parameters
* @param values array initializer containing StringLiterals for the parameter names
*/
protected void record(MethodDeclaration method, ArrayInitializer values) {
List<?> literals = values.expressions();
List<String> paramNames = new ArrayList<String>(literals.size());
for(int i= 0; i < literals.size(); i++) {
StringLiteral str = (StringLiteral) literals.get(i);
paramNames.add(str.getLiteralValue());
}
record(method, paramNames);
}
protected boolean isParameterAnnotation(String annotationType) {
return PARAMETER_ANNOTATION.equals(annotationType) || PARAMETER_ANNOTATION_FQN.equals(annotationType);
}
protected boolean isKnownAnnotation(String annotationType) {
return CONFIGURATION_ANNOTATION.equals(annotationType)
|| CONFIGURATION_ANNOTATION_FQN.equals(annotationType)
|| TEST_ANNOTATION.equals(annotationType)
|| TEST_ANNOTATION_FQN.equals(annotationType);
}
/**
* @return the non-null parameter name/value map
*/
public Map<String, String> getParametersMap() {
Map<String, String> parameterMap = new HashMap<>();
for (Entry<MethodDeclaration, List<String>> paramEntry : m_parameters.entrySet()) {
// get all '@Optional' value, as the default value for the parameters
List<String> optionals = new ArrayList<>();
for (Object p : paramEntry.getKey().parameters()) {
SingleVariableDeclaration paramDecl = (SingleVariableDeclaration) p;
for (Object pm : paramDecl.modifiers()) {
if (pm instanceof SingleMemberAnnotation) {
SingleMemberAnnotation paramAnn = (SingleMemberAnnotation) pm;
if ("Optional".equals(paramAnn.getTypeName().toString())) {
optionals.add(paramAnn.getValue().toString());
}
}
};
};
List<String> paramNames = paramEntry.getValue();
for(int i = 0; i < paramNames.size(); i++) {
String val = DEFAULT_PARAM_VALUE;
// the user is responsible for making sure the order of the @Optional parameters follow the order in @Parameters
if (i < optionals.size() && optionals.get(i) != null) {
val = optionals.get(i);
}
String paramName = paramNames.get(i);
if (parameterMap.containsKey(paramName) && !DEFAULT_PARAM_VALUE.equals(parameterMap.get(paramName))) {
continue;
}
parameterMap.put(paramName, val);
}
}
return parameterMap;
}
}